/*
 * =====================================================================================
 *
 *       Filename:  cl_thread.cxx
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  03/10/2011 01:21:24 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Chen Ken (Ken), zlchen.ken@gmail.com
 *        Company:  EMC
 *
 * =====================================================================================
 */

#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <cstring>
#include "cl_thread.hxx"

namespace neo
{
using namespace std;
namespace
{
__thread pid_t t_cachedTid = 0;

pid_t gettid(void)
{
  return static_cast<pid_t>(::syscall(SYS_gettid));
}
}

namespace CurrentThread
{
  pid_t clThreadId(void) 
	{
	  if(t_cachedTid == 0)
		{
      t_cachedTid = gettid();
		}
	  return t_cachedTid;
	}

  bool clIsMainThread(void) 
	{
    return clThreadId() == ::getpid();
	}
}

CLThread::CLThread(const CLCallbackFunctor& cb, bool isJoinable, bool isSysScope, size_t stackSize) 
  : m_callback(cb),
	m_thrState(CLThread::NOT_RUNNING),
	m_isJoinable(isJoinable),
	m_isSysScope(isSysScope),
	m_stackSize(stackSize)
{
	::bzero(&m_thrId, sizeof(m_thrId));
	::bzero(&m_thrAttr, sizeof(m_thrAttr));
}

CLThread::~CLThread()
{
  ::pthread_attr_destroy(&m_thrAttr);
}

bool CLThread::start(void)
{
  if(m_thrState == NOT_RUNNING)
  {
    ::pthread_attr_init(&m_thrAttr);
    setScope();
		setJoinable();

    int rval = ::pthread_create(&m_thrId, &m_thrAttr, CLThread::doStart, this); 
    if(rval == 0)
    {
      m_thrState = RUNNING;
    }

    return (rval == 0);
  }

  return true;
}

bool CLThread::join(void)
{
  void* status;
  int rval;
  rval = ::pthread_join(m_thrId, &status);

  return (rval == 0);
}
 
bool CLThread::isCurThread(void) const
{
	return ::pthread_equal(pthread_self(), m_thrId);
}

void* CLThread::doStart(void* arg)
{
  CLThread* thr = static_cast<CLThread*>(arg);
	::pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
  thr->run();

  return NULL;
}

void CLThread::run(void)
{
	m_callback();
}

void CLThread::setScope(void) 
{
  if(m_isSysScope)
  {
    ::pthread_attr_setscope(&m_thrAttr, PTHREAD_SCOPE_SYSTEM); 
  }
  else
  {
    ::pthread_attr_setscope(&m_thrAttr, PTHREAD_SCOPE_PROCESS); 
  }
}

void CLThread::setJoinable(void) 
{
  if(m_isJoinable)
  {
    ::pthread_attr_setdetachstate(&m_thrAttr, PTHREAD_CREATE_JOINABLE);
  }
  else
  {
    ::pthread_attr_setdetachstate(&m_thrAttr, PTHREAD_CREATE_DETACHED);
  }
}

void CLThread::setStackSize(void) 
{
  if(m_stackSize > 0)
  {
    ::pthread_attr_setstacksize(&m_thrAttr, m_stackSize);
  }
}

} // neo
